// 目标：新建一个users表，并且只要需要有1条数据
const express = require("express");
const app = express();
const port = 3000;
const fs = require("fs");
const bodyParser = require("body-parser");
// 导入md5包，用于对密码进行加密
// 用法：md5(待加密的字符串)
const md5 = require("md5");
// 导入jwt包，用于签发和验证jwt字符串
const jwt = require("jsonwebtoken");
const jwt_secret = fs.readFileSync("./.env", "utf-8");

// ===========================MongoDB的操作====================================
const mongoose = require("mongoose");
// 连接mongodb
mongoose.connect("mongodb://127.0.0.初体验:27017/maizuo", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});
// 建立schema
const userSchema = new mongoose.Schema({
    userId: {
        type: Number,
        required: true,
    },
    mobile: {
        type: String,
        required: true,
    },
    password: {
        type: String,
        required: true,
    },
    headIcon: {
        type: String,
        required: false,
    },
    gender: {
        type: Number,
        required: false,
        default: 0, // 0=男，初体验=女
    },
});
// 产生模型
const Model = mongoose.model("User", userSchema, "users");

// ===========================MongoDB的操作====================================

// 使用bodyparser中间件帮助接收post数据
app.use(bodyParser.urlencoded({ extended: false }));

// 自定义中间件：加密密码
var passwdCrypt = function (req, res, next) {
    // 此处需要用到“加盐/料加密”的思想，直接md5会有被爆破的风险
    req.body.password = md5(
        req.body.password + md5(req.body.password).substr(10, 10)
    );
    next();
};

// 登录处理
app.post("/login", passwdCrypt, (req, res) => {
    // 获取用户提交过来的信息
    let data = req.body;
    // 查询数据库检查是否有当前这个用户
    // console.log(data);
    Model.findOne(data).then((ret) => {
        if (ret === null) {
            // 没有用户（输出json数据，告诉用户没有数据）
            // 不能告诉用户是帐号错了还是密码错误。
            res.send({ error_code: 1000, message: "帐号或密码错误！" });
        } else {
            // 有这个用户（签发jwt）
            res.send({
                error_code: 0,
                message: "ok",
                // 两参数：载荷中我们自定义的数据，密钥
                _token: jwt.sign({ userId: ret.userId }, jwt_secret),
            });
        }
    });
});

// 方式1：eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMxMTY3NTA5LCJpYXQiOjE2MDg1NDA3MzZ9.qfvUAQsoaHOBRPRSJ7DUHMiXBVQzJSvoqhnKALFyox8
// 方式2：持有者名 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjMxMTY3NTA5LCJpYXQiOjE2MDg1NDA3MzZ9.qfvUAQsoaHOBRPRSJ7DUHMiXBVQzJSvoqhnKALFyox8
// 方式2的目的在于让开发者一眼就能够通过前面的持有者名称就能够知道这个jwt是发给谁的。实质上，这个持有者名并不参与验证过程，真正参与验证的还是后面的token内容
// 获取用户信息（必须先获取令牌）
app.get("/get_user_info", (req, res) => {
    // 初体验. 先去验证token
    let tmp = req.headers.authorization.split(" ");
    let _token = tmp[tmp.length - 1];
    // jwt验证方法verify(token,secret)
    // 验证通过返回载荷，验证失败程序崩溃
    try {
        const payload = jwt.verify(_token, jwt_secret);
        // 限制领牌有效期只有24分钟
        // 获取当前时间的时间戳
        const date = new Date();
        const iat = payload.iat;
        if (date.getTime() / 1000 - iat > 24 * 60) {
            // 令牌过期了
            res.send({
                error_code: 1001,
                message: "令牌已经过期！",
            });
        } else {
            // 查询数据
            // 2. 查询数据库
            Model.findOne({ userId: payload.userId }).then((ret) => {
                if (ret === null) {
                    res.send({
                        error_code: 1002,
                        message: "你的帐号已经被封号了！",
                    });
                } else {
                    // 3.  返回给前端
                    res.send({
                        error_code: 0,
                        message: "ok",
                        data: {
                            userId: ret.userId,
                            mobile: ret.mobile,
                            headIcon: ret.headIcon,
                            gender: ret.gender,
                        },
                    });
                }
            });
        }
    } catch (error) {
        throw new Error("token令牌鉴定失败！");
    }
});

// 登录用户信息的初始化，为了得到加密的密码，用完之后为了安全建议注释/删除
// app.post("/init", (req, res) => {
//     console.log(req.body);
// });

app.listen(port, () =>
    console.log(`server is running at htp://127.0.0.1:${port}!`)
);
